home *** CD-ROM | disk | FTP | other *** search
- // -------------------------------------------------------------------------------------
- // PaletteCell.m - image cell
- // -------------------------------------------------------------------------------------
- // Permission is granted to freely redistribute this source code, and to use fragments
- // of this code in your own applications if you find them to be useful. This class,
- // along with the source code, come with no warranty of any kind, and the user assumes
- // all responsibility for its use.
- // -------------------------------------------------------------------------------------
-
- extern "Objective-C" {
- #import <objc/objc.h>
- #import <appkit/appkit.h>
- #import <libc.h>
- #import <stdlib.h>
- #import <stdio.h>
- #import <string.h>
- #import <stdarg.h>
- #import <math.h>
- #import <dpsclient/wraps.h>
- #import "PaletteMatrix.h"
- #import "ImagePortfolio.h"
- #import "PaletteCell.h"
- #import "fileUtils.h"
- }
-
- // -------------------------------------------------------------------------------------
- // delegate selectors
- #define becameSELECTED @selector(cellDidBecomeSelected:)
- #define resignSELECTED @selector(cellDidResignSelected:)
-
- // -------------------------------------------------------------------------------------
- #define CELLOFFSET ((cFlags1.bordered || cFlags1.bezeled)?(cFlags1.bordered?2.0:3.0):0.0)
- #define HIGHLIGHTED ((BOOL)(cFlags1.highlighted || cFlags1.state))
-
- // -------------------------------------------------------------------------------------
- @implementation PaletteCell
-
- // -------------------------------------------------------------------------------------
- // support for multiple image types
- #define maxFILETYPES 32
- static char *imageFileExtn[maxFILETYPES + 1] = { 0 };
- static List *imageClassList = (List*)nil;
- static int imageClassCount = 0;
-
- /* add image class type */
- + addImageClass:classId
- {
-
- /* initialize */
- if (!imageClassList) imageClassList = [[[List alloc] initCount:1] empty];
-
- /* add class object */
- [imageClassList addObject:classId];
- imageClassCount = [imageClassList count];
-
- return self;
- }
-
- /* add valid image extension */
- + addImageExtensions:(const char*)ext1, ...
- {
- int i;
- char *cp;
- va_list args;
-
- /* check for catch-all */
- if (*ext1 == '*') {
- for (i = 0; imageFileExtn[i]; i++) free(imageFileExtn[i]);
- memset(imageFileExtn, 0, sizeof(imageFileExtn));
- imageFileExtn[0] = NXCopyStringBuffer("*");
- return self;
- }
-
- /* find end of list */
- for (i = 0; imageFileExtn[i]; i++);
-
- /* add first extension */
- imageFileExtn[i++] = NXCopyStringBuffer(ext1);
-
- /* add all remaining extensions */
- va_start(args, ext1);
- for (;cp = va_arg(args,char*);) imageFileExtn[i++] = NXCopyStringBuffer(cp);
- va_end(args);
-
- return self;
- }
-
- /* return true if extension is valid */
- + (BOOL)validExtension:(const char*)fileName
- {
- int i;
- char *extn = (char*)XFileExtension((char*)fileName);
- if (*imageFileExtn[0] == '*') return YES;
- for (i = 0; imageFileExtn[i]; i++) if (!strcasecmp(extn, imageFileExtn[i])) return YES;
- return NO;
- }
-
- // -------------------------------------------------------------------------------------
- // new cell methods
-
- /* old generic new cell */
- + new
- {
- return [self newIconCell];
- }
-
- /* generic new cell */
- + newIconCell
- {
- self = [self newIconCell:(char*)nil];
- [titleCell setStringValueNoCopy:""];
- return self;
- }
-
- /* new cell with title */
- + newIconCell:(const char *)aString
- {
- return [[self allocFromZone:NXDefaultMallocZone()] initIconCell:aString];
- }
-
- /* generic init */
- - init
- {
- [self initIconCell:(char*)nil];
- [titleCell setStringValueNoCopy:""];
- return self;
- }
-
- /* init with title */
- - initIconCell:(const char *)aString
- {
-
- /* init instance */
- [super initIconCell:(char*)nil];
- [self setStringValueNoCopy:""];
- [self setAlignment:NX_CENTERED];
- titleCell = [[Cell allocFromZone:[self zone]] initTextCell:aString];
- [titleCell setAlignment:NX_CENTERED];
- [self setBezeled:NO];
-
- /* init vars */
- cFlags1.editable = NO;
- cFlags1.selectable = NO;
- cFlags2.noWrap = YES;
- cFlags2._isLeaf = YES;
- delegate = self;
- dragMode = NO;
-
- imageId = (id)nil;
- smallImage = (id)nil;
- imagePath = (char*)nil;
- imageMutex = mutex_alloc();
-
- return self;
- }
-
- /* make an empty copy of this cell */
- - copy
- {
- NXZone *zone = [self zone];
- id tCell = titleCell;
- self = [super copyFromZone:zone];
- titleCell = [[tCell copyFromZone:zone] setFont:[tCell font]];
- imageMutex = mutex_alloc();
- imageId = (id)nil;
- smallImage = (id)nil;
- imagePath = (char*)nil;
- return self;
- }
-
- /* free this cell */
- - free
- {
- if (imagePath) free(imagePath);
- if (imageId) [imageId free];
- if (smallImage) [smallImage free];
- [titleCell free];
- mutex_free(imageMutex);
- return [super free];
- }
-
- /* set matrix owner id */
- - setDelegate:anObject
- {
- delegate = anObject;
- return self;
- }
-
- /* set drag mode */
- - setDragMode:(BOOL)flag
- {
- dragMode = flag;
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // set cell size
-
- - setCellSize:(const NXSize*)cellSize
- {
- if (smallImage) { [smallImage free]; smallImage = (id)nil; }
- return self;
- }
-
- - setFont:fontId
- {
- return [titleCell setFont:fontId];
- }
-
- // -------------------------------------------------------------------------------------
- // image methods
-
- /* load image specified in preset file name */
- - loadImageFile
- {
- int i;
- id tempId = (id)nil;
-
- /* validate load */
- if (!imagePath) return (id)nil; // no file specified
- if (imageId) return imageId; // already loaded
-
- /* try all image classes until we find one we like */
- for (i = 0; !tempId && (i < imageClassCount); i++) {
- id imClass = [imageClassList objectAt:i];
- if (imClass == [NXImage class]) { // note: doesn't allow for subclassing NXImage
- if (!(tempId = [[imClass alloc] initFromFile:imagePath])) continue;
- if (![tempId lastRepresentation]) {
- [tempId free];
- tempId = (id)nil;
- continue;
- }
- } else {
- int j;
- NXSize size;
- id repList = [imClass newListFromFile:imagePath];
- if (!repList) continue;
- if (![repList count]) { [repList free]; continue; }
- [[repList objectAt:0] getSize:&size];
- if (!size.width || !size.height) {
- [repList freeObjects];
- [repList free];
- continue;
- }
- tempId = [[NXImage alloc] init];
- for (j = 0; j < (int)[repList count]; j++)
- [tempId useRepresentation:[repList objectAt:j]];
- [repList free];
- }
- mutex_lock(imageMutex);
- imageId = tempId;
- mutex_unlock(imageMutex);
- break;
- }
-
- return tempId;
- }
-
- /* open image (may be called from non-main thread) */
- - setImageFile:(const char *)filePath
- {
- char iName[MAXPATHLEN + 1], *p;
-
- /* free prior storage (actually this should never be necessary!) */
- mutex_lock(imageMutex);
- if (imagePath ) { free(imagePath); imagePath = (char*)nil; }
- if (imageId ) { [imageId free]; imageId = (id)nil; }
- if (smallImage) { [smallImage free]; smallImage = (id)nil; }
- mutex_unlock(imageMutex);
-
- /* save filename and title */
- imagePath = NXCopyStringBuffer(filePath);
- strcpy(iName, XFileNameExtension(filePath));
- if (p = rindex(iName, '.')) *p = 0;
- [titleCell setStringValue:iName]; // OK since it can't draw yet
-
- /* open new image and make sure it was readable */
- if (![self mainThreadPerform:@selector(loadImageFile) wait:YES]) return (id)nil;
- return self;
-
- }
-
- /* return image id */
- - image
- {
- id image;
- if (!imagePath) return (id)nil;
- mutex_lock(imageMutex);
- image = imageId;
- mutex_unlock(imageMutex);
- return image;
- }
-
- /* return image representation */
- - imageRepresentation:(int)imageNum
- {
- id repList, image = [self image];
- if (!image) return (id)nil;
- repList = [image representationList];
- return [repList objectAt:(imageNum % [repList count])];
- }
-
- /* return path to image */
- - (const char*)imagePath
- {
- return imagePath;
- }
-
- /* return cell title */
- - (const char*)cellTitle
- {
- return [titleCell stringValue];
- }
-
- // -------------------------------------------------------------------------------------
-
- - calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect
- {
- NXRect rect = *aRect;
- NXSize titleSize;
- theSize->width = 1.0;
- theSize->height = 1.0;
- [titleCell calcCellSize:&titleSize inRect:&rect];
- theSize->width += titleSize.width;
- theSize->height = MAX(titleSize.height, theSize->height);
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // highlight cell
-
- /* set selected state */
- - setState:(int)flag
- {
- if (![self image]) return self; // ignore if no image in cell
- if ((!cFlags1.state && flag) || (cFlags1.state && !flag)) {
- cFlags1.state = flag? YES : NO;
- if (flag) {
- if (delegate && [delegate respondsTo:@selector(cellBecameSelected:)])
- [delegate cellBecameSelected:self];
- } else {
- if (delegate && [delegate respondsTo:@selector(cellResignedSelected:)])
- [delegate cellResignedSelected:self];
- }
- }
- return self;
- }
-
- /* highlight and select */
- - highlight:(const NXRect*)cellFrame inView:controlView lit:(BOOL)flag
- {
- if (![self image]) return self; // ignore if no image in cell
- if ((!cFlags1.highlighted && flag) || (cFlags1.highlighted && !flag)) {
- cFlags1.highlighted = flag? YES : NO;
- [self setState:cFlags1.highlighted];
- [self drawSelf:cellFrame inView:controlView];
- }
- return self;
- }
-
- /* return highlight state */
- - (BOOL)isHighlighted
- {
- return HIGHLIGHTED;
- }
-
- /* return selected state */
- - (BOOL)isSelected
- {
- return (BOOL)cFlags1.state;
- }
-
- // -------------------------------------------------------------------------------------
- // tool methods
-
- /* resize image (only if newSize is smaller than original) (MAIN THREAD ONLY!) */
- - _resizeImage:image:(int)num toSize:(NXSize*)toSize
- {
- float aspect;
- NXRect r = { { 0.0, 0.0 }, { 0.0, 0.0 } };
- id newImage;
-
- /* return if already within bounds */
- if (!image) return (id)nil;
- [image getSize:&r.size];
- if ((r.size.width <= toSize->width) && (r.size.height <= toSize->height)) return (id)nil;
-
- /* calculate new bounds */
- aspect = r.size.width / r.size.height;
- if (r.size.width > toSize->width ) r.size.width = toSize->width ;
- if (r.size.height > toSize->height) r.size.height = toSize->height;
- if (r.size.width > r.size.height * aspect) r.size.width = r.size.height * aspect;
- if (r.size.height > r.size.width / aspect) r.size.height = r.size.width / aspect;
-
- /* create a new image */
- newImage = [[NXImage alloc] initSize:&r.size];
- if (!newImage) {
- NXLogError("ImagePortfolio: Unable to init scaled image");
- return (id)nil;
- }
- [newImage setFlipped:[image isFlipped]];
-
- /* draw scaled image */
- PSgsave();
- if ([newImage lockFocus]) {
- [[self imageRepresentation:num] drawIn:&r];
- [newImage unlockFocus];
- } else {
- [newImage free];
- newImage = (id)nil;
- }
- PSgrestore();
-
- /* return resize image */
- return newImage;
-
- }
-
- // -------------------------------------------------------------------------------------
- // smallImage support
-
- /* calculate bounding size for small image */
- - calcImageSize:(NXSize*)imageSize forCellSize:(NXSize*)cellSize
- {
- NXSize tSize;
- NXRect cFrame = { {0.0, 0.0}, {0.0, 0.0} };
- cFrame.size = *cellSize;
- NXInsetRect(&cFrame, 1.0, 1.0);
- [titleCell calcCellSize:&tSize inRect:&cFrame];
- cFrame.size.height -= tSize.height + 2.0;
- *imageSize = cFrame.size;
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // draw methods
-
- - drawSelf:(const NXRect *)cellFrame inView:controlView
- {
- return [self drawInside:cellFrame inView:controlView];
- }
-
- - drawInside:(const NXRect *)cellFrame inView:controlView
- {
- NXSize tSize = {0.0, 0.0}, iSize;
- NXRect rect, cFrame = *cellFrame;
- float offset = CELLOFFSET;
- BOOL multi = NO;
- id image;
-
- /* check for drag mode */
- if (dragMode) {
- PSsetgray((NX_LTGRAY + NX_DKGRAY) / 2.0);
- NXRectFill(cellFrame);
- return self;
- }
-
- /* draw border */
- if (cFlags1.bordered) { PSsetgray(NX_BLACK); NXFrameRectWithWidth(cellFrame, 1.0); } else
- if (cFlags1.bezeled ) { NXDrawGrayBezel(cellFrame, (NXRect*)nil); }
-
- /* inset rectangle and draw highlight */
- NXInsetRect(&cFrame, offset, offset);
- PSsetgray((HIGHLIGHTED)? NX_WHITE: NX_LTGRAY);
- NXRectFill(&cFrame);
- NXInsetRect(&cFrame, 1.0, 1.0);
-
- /* calc text size */
- [titleCell calcCellSize:&tSize inRect:&cFrame];
-
- /* resize & draw image */
- if ([self image]) {
- rect = cFrame;
- rect.size.height -= tSize.height + 2.0;
- rect.origin.y += rect.size.height;
- if ([[imageId representationList] count] > 1) multi = YES;
- if (!smallImage) smallImage = [self _resizeImage:imageId:0 toSize:&rect.size];
- image = (smallImage)? smallImage : imageId;
- [image getSize:&iSize];
- rect.origin.y -= (rect.size.height - iSize.height) / 2.0;
- rect.origin.x += (rect.size.width - iSize.width ) / 2.0;
- [image composite:NX_SOVER toPoint:&rect.origin];
- }
-
- /* draw image title cell */
- rect = cFrame;
- rect.origin.y += rect.size.height - tSize.height - 1;
- rect.origin.x += floor((rect.size.width - tSize.width) / 2.0);
- rect.size.width = tSize.width;
- rect.size.height = tSize.height;
- [titleCell drawSelf:&rect inView:controlView];
-
- return self;
- }
-
- @end
-